/******************** (C) COPYRIGHT 2008 STMicroelectronics ********************
* File Name          : msd.c
* Author             : MCD Application Team
* Version            : V2.1
* Date               : 05/30/2008
* Description        : MSD card driver source file.
*                      Pin assignment:
*             ----------------------------------------------
*             |  STM32F10x    |     MSD          Pin        |
*             ----------------------------------------------
*             | P0.4          |   ChipSelect      1         |
*             | P0.1 / MOSI   |   DataIn          2         |
*             |               |   GND             3 (0 V)   |
*             |               |   VDD             4 (3.3 V) |
*             | P0.2 / SCLK   |   Clock           5         |
*             |               |   GND             6 (0 V)   |
*             | P0.0 / MISO   |   DataOut         7         |
*             -----------------------------------------------
********************************************************************************
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME.
* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
* CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
* FOR MORE INFORMATION PLEASE CAREFULLY READ THE LICENSE AGREEMENT FILE LOCATED
* IN THE ROOT DIRECTORY OF THIS FIRMWARE PACKAGE.
*******************************************************************************/

/* Includes ------------------------------------------------------------------*/
#include "msd.h"
#include <stm32f10x_spi.h>

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Select MSD Card: ChipSelect pin low  */
#define MSD_CS_LOW()    GPIO_ResetBits(GPIOA, GPIO_Pin_4)
/* Deselect MSD Card: ChipSelect pin high */
#define MSD_CS_HIGH()   GPIO_SetBits(GPIOA, GPIO_Pin_4)
#define MSD_CS_PIN      GPIO_Pin_4
#define MSD_CS_PORT     GPIOA
#define RCC_APB2Periph_MSD_CS  RCC_APB2Periph_GPIOA

#define MSD_SPI         SPI1
#define MSD_RCC_SPI     RCC_APB2Periph_SPI1

/* SD detected pin */
#define SD_CD_PORT       GPIOB
#define SD_CD_PIN        GPIO_Pin_0
#define RCC_APB2Periph_SD_CD    RCC_APB2Periph_GPIOB

/* SD power pin */
#define SD_PWR_PORT       GPIOC
#define SD_PWR_PIN        GPIO_Pin_4
#define RCC_APB2Periph_SD_PWR    RCC_APB2Periph_GPIOC
#define sd_power_on()     GPIO_ResetBits(SD_PWR_PORT, SD_PWR_PIN)

/* Private function prototypes -----------------------------------------------*/
static void SPI_Config(void);
/* Private functions ---------------------------------------------------------*/

/*******************************************************************************
* Function Name  : MSD_Init
* Description    : Initializes the MSD/SD communication.
* Input          : None
* Output         : None
* Return         : The MSD Response: - MSD_RESPONSE_FAILURE: Sequence failed
*                                    - MSD_RESPONSE_NO_ERROR: Sequence succeed
*******************************************************************************/
u8 MSD_Init(void)
{
    u32 i = 0;

    /* Initialize SPI */
    SPI_Config();
    /* MSD chip select high */
    MSD_CS_HIGH();
    /* Send dummy byte 0xFF, 10 times with CS high*/
    /* rise CS and MOSI for 80 clocks cycles */
    for (i = 0; i <= 9; i++)
    {
        /* Send dummy byte 0xFF */
        MSD_WriteByte(DUMMY);
    }
    /*------------Put MSD in SPI mode--------------*/
    /* MSD initialized and set to SPI mode properly */
    return (MSD_GoIdleState());
}

/*******************************************************************************
* Function Name  : MSD_WriteBlock
* Description    : Writes a block on the MSD
* Input          : - pBuffer : pointer to the buffer containing the data to be
*                    written on the MSD.
*                  - WriteAddr : address to write on.
*                  - NumByteToWrite: number of data to write
* Output         : None
* Return         : The MSD Response: - MSD_RESPONSE_FAILURE: Sequence failed
*                                    - MSD_RESPONSE_NO_ERROR: Sequence succeed
*******************************************************************************/
u8 MSD_WriteBlock(u8 *pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
    u32 i = 0;
    u8 rvalue = MSD_RESPONSE_FAILURE;

    /* MSD chip select low */
    MSD_CS_LOW();
    /* Send CMD24 (MSD_WRITE_BLOCK) to write multiple block */
    MSD_SendCmd(MSD_WRITE_BLOCK, WriteAddr, 0xFF);

    /* Check if the MSD acknowledged the write block command: R1 response (0x00: no errors) */
    if (!MSD_GetResponse(MSD_RESPONSE_NO_ERROR))
    {
        /* Send a dummy byte */
        MSD_WriteByte(DUMMY);
        /* Send the data token to signify the start of the data */
        MSD_WriteByte(0xFE);
        /* Write the block data to MSD : write count data by block */
        for (i = 0; i < NumByteToWrite; i++)
        {
            /* Send the pointed byte */
            MSD_WriteByte(*pBuffer);
            /* Point to the next location where the byte read will be saved */
            pBuffer++;
        }
        /* Put CRC bytes (not really needed by us, but required by MSD) */
        MSD_ReadByte();
        MSD_ReadByte();
        /* Read data response */
        if (MSD_GetDataResponse() == MSD_DATA_OK)
        {
            rvalue = MSD_RESPONSE_NO_ERROR;
        }
    }

    /* MSD chip select high */
    MSD_CS_HIGH();
    /* Send dummy byte: 8 Clock pulses of delay */
    MSD_WriteByte(DUMMY);
    /* Returns the reponse */
    return rvalue;
}

/*******************************************************************************
* Function Name  : MSD_ReadBlock
* Description    : Reads a block of data from the MSD.
* Input          : - pBuffer : pointer to the buffer that receives the data read
*                    from the MSD.
*                  - ReadAddr : MSD's internal address to read from.
*                  - NumByteToRead : number of bytes to read from the MSD.
* Output         : None
* Return         : The MSD Response: - MSD_RESPONSE_FAILURE: Sequence failed
*                                    - MSD_RESPONSE_NO_ERROR: Sequence succeed
*******************************************************************************/
u8 MSD_ReadBlock(u8 *pBuffer, u32 ReadAddr, u16 NumByteToRead)
{
    u32 i = 0;
    u8 rvalue = MSD_RESPONSE_FAILURE;

    /* MSD chip select low */
    MSD_CS_LOW();
    /* Send CMD17 (MSD_READ_SINGLE_BLOCK) to read one block */
    MSD_SendCmd(MSD_READ_SINGLE_BLOCK, ReadAddr, 0xFF);

    /* Check if the MSD acknowledged the read block command: R1 response (0x00: no errors) */
    if (!MSD_GetResponse(MSD_RESPONSE_NO_ERROR))
    {
        /* Now look for the data token to signify the start of the data */
        if (!MSD_GetResponse(MSD_START_DATA_SINGLE_BLOCK_READ))
        {
            /* Read the MSD block data : read NumByteToRead data */
            for (i = 0; i < NumByteToRead; i++)
            {
                /* Save the received data */
                *pBuffer = MSD_ReadByte();
                /* Point to the next location where the byte read will be saved */
                pBuffer++;
            }
            /* Get CRC bytes (not really needed by us, but required by MSD) */
            MSD_ReadByte();
            MSD_ReadByte();
            /* Set response value to success */
            rvalue = MSD_RESPONSE_NO_ERROR;
        }
    }

    /* MSD chip select high */
    MSD_CS_HIGH();
    /* Send dummy byte: 8 Clock pulses of delay */
    MSD_WriteByte(DUMMY);
    /* Returns the reponse */
    return rvalue;
}

/*******************************************************************************
* Function Name  : MSD_WriteBuffer
* Description    : Writes many blocks on the MSD
* Input          : - pBuffer : pointer to the buffer containing the data to be
*                    written on the MSD.
*                  - WriteAddr : address to write on.
*                  - NumByteToWrite: number of data to write
* Output         : None
* Return         : The MSD Response: - MSD_RESPONSE_FAILURE: Sequence failed
*                                    - MSD_RESPONSE_NO_ERROR: Sequence succeed
*******************************************************************************/
u8 MSD_WriteBuffer(u8 *pBuffer, u32 WriteAddr, u32 NumByteToWrite)
{
    u32 i = 0, NbrOfBlock = 0, Offset = 0;
    u8 rvalue = MSD_RESPONSE_FAILURE;

    /* Calculate number of blocks to write */
    NbrOfBlock = NumByteToWrite / BLOCK_SIZE;
    /* MSD chip select low */
    MSD_CS_LOW();

    /* Data transfer */
    while (NbrOfBlock --)
    {
        /* Send CMD24 (MSD_WRITE_BLOCK) to write blocks */
        MSD_SendCmd(MSD_WRITE_BLOCK, WriteAddr + Offset, 0xFF);

        /* Check if the MSD acknowledged the write block command: R1 response (0x00: no errors) */
        if (MSD_GetResponse(MSD_RESPONSE_NO_ERROR))
        {
            return MSD_RESPONSE_FAILURE;
        }
        /* Send dummy byte */
        MSD_WriteByte(DUMMY);
        /* Send the data token to signify the start of the data */
        MSD_WriteByte(MSD_START_DATA_SINGLE_BLOCK_WRITE);
        /* Write the block data to MSD : write count data by block */
        for (i = 0; i < BLOCK_SIZE; i++)
        {
            /* Send the pointed byte */
            MSD_WriteByte(*pBuffer);
            /* Point to the next location where the byte read will be saved */
            pBuffer++;
        }
        /* Set next write address */
        Offset += 512;
        /* Put CRC bytes (not really needed by us, but required by MSD) */
        MSD_ReadByte();
        MSD_ReadByte();
        /* Read data response */
        if (MSD_GetDataResponse() == MSD_DATA_OK)
        {
            /* Set response value to success */
            rvalue = MSD_RESPONSE_NO_ERROR;
        }
        else
        {
            /* Set response value to failure */
            rvalue = MSD_RESPONSE_FAILURE;
        }
    }

    /* MSD chip select high */
    MSD_CS_HIGH();
    /* Send dummy byte: 8 Clock pulses of delay */
    MSD_WriteByte(DUMMY);
    /* Returns the reponse */
    return rvalue;
}

/*******************************************************************************
* Function Name  : MSD_ReadBuffer
* Description    : Reads multiple block of data from the MSD.
* Input          : - pBuffer : pointer to the buffer that receives the data read
*                    from the MSD.
*                  - ReadAddr : MSD's internal address to read from.
*                  - NumByteToRead : number of bytes to read from the MSD.
* Output         : None
* Return         : The MSD Response: - MSD_RESPONSE_FAILURE: Sequence failed
*                                    - MSD_RESPONSE_NO_ERROR: Sequence succeed
*******************************************************************************/
u8 MSD_ReadBuffer(u8 *pBuffer, u32 ReadAddr, u32 NumByteToRead)
{
    u32 i = 0, NbrOfBlock = 0, Offset = 0;
    u8 rvalue = MSD_RESPONSE_FAILURE;

    /* Calculate number of blocks to read */
    NbrOfBlock = NumByteToRead / BLOCK_SIZE;
    /* MSD chip select low */
    MSD_CS_LOW();

    /* Data transfer */
    while (NbrOfBlock --)
    {
        /* Send CMD17 (MSD_READ_SINGLE_BLOCK) to read one block */
        MSD_SendCmd(MSD_READ_SINGLE_BLOCK, ReadAddr + Offset, 0xFF);
        /* Check if the MSD acknowledged the read block command: R1 response (0x00: no errors) */
        if (MSD_GetResponse(MSD_RESPONSE_NO_ERROR))
        {
            return  MSD_RESPONSE_FAILURE;
        }
        /* Now look for the data token to signify the start of the data */
        if (!MSD_GetResponse(MSD_START_DATA_SINGLE_BLOCK_READ))
        {
            /* Read the MSD block data : read NumByteToRead data */
            for (i = 0; i < BLOCK_SIZE; i++)
            {
                /* Read the pointed data */
                *pBuffer = MSD_ReadByte();
                /* Point to the next location where the byte read will be saved */
                pBuffer++;
            }
            /* Set next read address*/
            Offset += 512;
            /* get CRC bytes (not really needed by us, but required by MSD) */
            MSD_ReadByte();
            MSD_ReadByte();
            /* Set response value to success */
            rvalue = MSD_RESPONSE_NO_ERROR;
        }
        else
        {
            /* Set response value to failure */
            rvalue = MSD_RESPONSE_FAILURE;
        }
    }

    /* MSD chip select high */
    MSD_CS_HIGH();
    /* Send dummy byte: 8 Clock pulses of delay */
    MSD_WriteByte(DUMMY);
    /* Returns the reponse */
    return rvalue;
}

/*******************************************************************************
* Function Name  : MSD_GetCSDRegister
* Description    : Read the CSD card register.
*                  Reading the contents of the CSD register in SPI mode
*                  is a simple read-block transaction.
* Input          : - MSD_csd: pointer on an SCD register structure
* Output         : None
* Return         : The MSD Response: - MSD_RESPONSE_FAILURE: Sequence failed
*                                    - MSD_RESPONSE_NO_ERROR: Sequence succeed
*******************************************************************************/
u8 MSD_GetCSDRegister(sMSD_CSD *MSD_csd)
{
    u32 i = 0;
    u8 rvalue = MSD_RESPONSE_FAILURE;
    u8 CSD_Tab[16];

    /* MSD chip select low */
    MSD_CS_LOW();
    /* Send CMD9 (CSD register) or CMD10(CSD register) */
    MSD_SendCmd(MSD_SEND_CSD, 0, 0xFF);

    /* Wait for response in the R1 format (0x00 is no errors) */
    if (!MSD_GetResponse(MSD_RESPONSE_NO_ERROR))
    {
        if (!MSD_GetResponse(MSD_START_DATA_SINGLE_BLOCK_READ))
        {
            for (i = 0; i < 16; i++)
            {
                /* Store CSD register value on CSD_Tab */
                CSD_Tab[i] = MSD_ReadByte();
            }
        }
        /* Get CRC bytes (not really needed by us, but required by MSD) */
        MSD_WriteByte(DUMMY);
        MSD_WriteByte(DUMMY);
        /* Set response value to success */
        rvalue = MSD_RESPONSE_NO_ERROR;
    }

    /* MSD chip select high */
    MSD_CS_HIGH();
    /* Send dummy byte: 8 Clock pulses of delay */
    MSD_WriteByte(DUMMY);

    /* Byte 0 */
    MSD_csd->CSDStruct = (CSD_Tab[0] & 0xC0) >> 6;
    MSD_csd->SysSpecVersion = (CSD_Tab[0] & 0x3C) >> 2;
    MSD_csd->Reserved1 = CSD_Tab[0] & 0x03;
    /* Byte 1 */
    MSD_csd->TAAC = CSD_Tab[1] ;
    /* Byte 2 */
    MSD_csd->NSAC = CSD_Tab[2];
    /* Byte 3 */
    MSD_csd->MaxBusClkFrec = CSD_Tab[3];
    /* Byte 4 */
    MSD_csd->CardComdClasses = CSD_Tab[4] << 4;
    /* Byte 5 */
    MSD_csd->CardComdClasses |= (CSD_Tab[5] & 0xF0) >> 4;
    MSD_csd->RdBlockLen = CSD_Tab[5] & 0x0F;
    /* Byte 6 */
    MSD_csd->PartBlockRead = (CSD_Tab[6] & 0x80) >> 7;
    MSD_csd->WrBlockMisalign = (CSD_Tab[6] & 0x40) >> 6;
    MSD_csd->RdBlockMisalign = (CSD_Tab[6] & 0x20) >> 5;
    MSD_csd->DSRImpl = (CSD_Tab[6] & 0x10) >> 4;
    MSD_csd->Reserved2 = 0; /* Reserved */
    MSD_csd->DeviceSize = (CSD_Tab[6] & 0x03) << 10;
    /* Byte 7 */
    MSD_csd->DeviceSize |= (CSD_Tab[7]) << 2;
    /* Byte 8 */
    MSD_csd->DeviceSize |= (CSD_Tab[8] & 0xC0) >> 6;
    MSD_csd->MaxRdCurrentVDDMin = (CSD_Tab[8] & 0x38) >> 3;
    MSD_csd->MaxRdCurrentVDDMax = (CSD_Tab[8] & 0x07);
    /* Byte 9 */
    MSD_csd->MaxWrCurrentVDDMin = (CSD_Tab[9] & 0xE0) >> 5;
    MSD_csd->MaxWrCurrentVDDMax = (CSD_Tab[9] & 0x1C) >> 2;
    MSD_csd->DeviceSizeMul = (CSD_Tab[9] & 0x03) << 1;
    /* Byte 10 */
    MSD_csd->DeviceSizeMul |= (CSD_Tab[10] & 0x80) >> 7;
    MSD_csd->EraseGrSize = (CSD_Tab[10] & 0x7C) >> 2;
    MSD_csd->EraseGrMul = (CSD_Tab[10] & 0x03) << 3;
    /* Byte 11 */
    MSD_csd->EraseGrMul |= (CSD_Tab[11] & 0xE0) >> 5;
    MSD_csd->WrProtectGrSize = (CSD_Tab[11] & 0x1F);
    /* Byte 12 */
    MSD_csd->WrProtectGrEnable = (CSD_Tab[12] & 0x80) >> 7;
    MSD_csd->ManDeflECC = (CSD_Tab[12] & 0x60) >> 5;
    MSD_csd->WrSpeedFact = (CSD_Tab[12] & 0x1C) >> 2;
    MSD_csd->MaxWrBlockLen = (CSD_Tab[12] & 0x03) << 2;
    /* Byte 13 */
    MSD_csd->MaxWrBlockLen |= (CSD_Tab[13] & 0xc0) >> 6;
    MSD_csd->WriteBlockPaPartial = (CSD_Tab[13] & 0x20) >> 5;
    MSD_csd->Reserved3 = 0;
    MSD_csd->ContentProtectAppli = (CSD_Tab[13] & 0x01);
    /* Byte 14 */
    MSD_csd->FileFormatGrouop = (CSD_Tab[14] & 0x80) >> 7;
    MSD_csd->CopyFlag = (CSD_Tab[14] & 0x40) >> 6;
    MSD_csd->PermWrProtect = (CSD_Tab[14] & 0x20) >> 5;
    MSD_csd->TempWrProtect = (CSD_Tab[14] & 0x10) >> 4;
    MSD_csd->FileFormat = (CSD_Tab[14] & 0x0C) >> 2;
    MSD_csd->ECC = (CSD_Tab[14] & 0x03);
    /* Byte 15 */
    MSD_csd->msd_CRC = (CSD_Tab[15] & 0xFE) >> 1;
    MSD_csd->Reserved4 = 1;

    /* Return the reponse */
    return rvalue;
}

/*******************************************************************************
* Function Name  : MSD_GetCIDRegister
* Description    : Read the CID card register.
*                  Reading the contents of the CID register in SPI mode
*                  is a simple read-block transaction.
* Input          : - MSD_cid: pointer on an CID register structure
* Output         : None
* Return         : The MSD Response: - MSD_RESPONSE_FAILURE: Sequence failed
*                                    - MSD_RESPONSE_NO_ERROR: Sequence succeed
*******************************************************************************/
u8 MSD_GetCIDRegister(sMSD_CID *MSD_cid)
{
    u32 i = 0;
    u8 rvalue = MSD_RESPONSE_FAILURE;
    u8 CID_Tab[16];

    /* MSD chip select low */
    MSD_CS_LOW();
    /* Send CMD10 (CID register) */
    MSD_SendCmd(MSD_SEND_CID, 0, 0xFF);

    /* Wait for response in the R1 format (0x00 is no errors) */
    if (!MSD_GetResponse(MSD_RESPONSE_NO_ERROR))
    {
        if (!MSD_GetResponse(MSD_START_DATA_SINGLE_BLOCK_READ))
        {
            /* Store CID register value on CID_Tab */
            for (i = 0; i < 16; i++)
            {
                CID_Tab[i] = MSD_ReadByte();
            }
        }
        /* Get CRC bytes (not really needed by us, but required by MSD) */
        MSD_WriteByte(DUMMY);
        MSD_WriteByte(DUMMY);
        /* Set response value to success */
        rvalue = MSD_RESPONSE_NO_ERROR;
    }

    /* MSD chip select high */
    MSD_CS_HIGH();
    /* Send dummy byte: 8 Clock pulses of delay */
    MSD_WriteByte(DUMMY);

    /* Byte 0 */
    MSD_cid->ManufacturerID = CID_Tab[0];
    /* Byte 1 */
    MSD_cid->OEM_AppliID = CID_Tab[1] << 8;
    /* Byte 2 */
    MSD_cid->OEM_AppliID |= CID_Tab[2];
    /* Byte 3 */
    MSD_cid->ProdName1 = CID_Tab[3] << 24;
    /* Byte 4 */
    MSD_cid->ProdName1 |= CID_Tab[4] << 16;
    /* Byte 5 */
    MSD_cid->ProdName1 |= CID_Tab[5] << 8;
    /* Byte 6 */
    MSD_cid->ProdName1 |= CID_Tab[6];
    /* Byte 7 */
    MSD_cid->ProdName2 = CID_Tab[7];
    /* Byte 8 */
    MSD_cid->ProdRev = CID_Tab[8];
    /* Byte 9 */
    MSD_cid->ProdSN = CID_Tab[9] << 24;
    /* Byte 10 */
    MSD_cid->ProdSN |= CID_Tab[10] << 16;
    /* Byte 11 */
    MSD_cid->ProdSN |= CID_Tab[11] << 8;
    /* Byte 12 */
    MSD_cid->ProdSN |= CID_Tab[12];
    /* Byte 13 */
    MSD_cid->Reserved1 |= (CID_Tab[13] & 0xF0) >> 4;
    /* Byte 14 */
    MSD_cid->ManufactDate = (CID_Tab[13] & 0x0F) << 8;
    /* Byte 15 */
    MSD_cid->ManufactDate |= CID_Tab[14];
    /* Byte 16 */
    MSD_cid->msd_CRC = (CID_Tab[15] & 0xFE) >> 1;
    MSD_cid->Reserved2 = 1;

    /* Return the reponse */
    return rvalue;
}

/*******************************************************************************
* Function Name  : MSD_SendCmd
* Description    : Send 5 bytes command to the MSD card.
* Input          : - Cmd: the user expected command to send to MSD card
*                  - Arg: the command argument
*                  - Crc: the CRC
* Output         : None
* Return         : None
*******************************************************************************/
void MSD_SendCmd(u8 Cmd, u32 Arg, u8 Crc)
{
    u32 i = 0x00;
    u8 Frame[6];

    /* Construct byte1 */
    Frame[0] = (Cmd | 0x40);
    /* Construct byte2 */
    Frame[1] = (u8)(Arg >> 24);
    /* Construct byte3 */
    Frame[2] = (u8)(Arg >> 16);
    /* Construct byte4 */
    Frame[3] = (u8)(Arg >> 8);
    /* Construct byte5 */
    Frame[4] = (u8)(Arg);
    /* Construct CRC: byte6 */
    Frame[5] = (Crc);

    /* Send the Cmd bytes */
    for (i = 0; i < 6; i++)
    {
        MSD_WriteByte(Frame[i]);
    }
}

/*******************************************************************************
* Function Name  : MSD_GetDataResponse
* Description    : Get MSD card data response.
* Input          : None
* Output         : None
* Return         : The MSD status: Read data response xxx0<status>1
*                   - status 010: Data accecpted
*                   - status 101: Data rejected due to a crc error
*                   - status 110: Data rejected due to a Write error.
*                   - status 111: Data rejected due to other error.
*******************************************************************************/
u8 MSD_GetDataResponse(void)
{
    u32 i = 0;
    u8 response, rvalue;

    while (i <= 64)
    {
        /* Read resonse */
        response = MSD_ReadByte();
        /* Mask unused bits */
        response &= 0x1F;

        switch (response)
        {
        case MSD_DATA_OK:
        {
            rvalue = MSD_DATA_OK;
            break;
        }

        case MSD_DATA_CRC_ERROR:
            return MSD_DATA_CRC_ERROR;

        case MSD_DATA_WRITE_ERROR:
            return MSD_DATA_WRITE_ERROR;

        default:
        {
            rvalue = MSD_DATA_OTHER_ERROR;
            break;
        }
        }
        /* Exit loop in case of data ok */
        if (rvalue == MSD_DATA_OK)
            break;
        /* Increment loop counter */
        i++;
    }
    /* Wait null data */
    while (MSD_ReadByte() == 0);
    /* Return response */
    return response;
}

/*******************************************************************************
* Function Name  : MSD_GetResponse
* Description    : Returns the MSD response.
* Input          : None
* Output         : None
* Return         : The MSD Response: - MSD_RESPONSE_FAILURE: Sequence failed
*                                    - MSD_RESPONSE_NO_ERROR: Sequence succeed
*******************************************************************************/
u8 MSD_GetResponse(u8 Response)
{
    u32 Count = 0xFFF;

    /* Check if response is got or a timeout is happen */
    while ((MSD_ReadByte() != Response) && Count)
    {
        Count--;
    }

    if (Count == 0)
    {
        /* After time out */
        return MSD_RESPONSE_FAILURE;
    }
    else
    {
        /* Right response got */
        return MSD_RESPONSE_NO_ERROR;
    }
}

/*******************************************************************************
* Function Name  : MSD_GetStatus
* Description    : Returns the MSD status.
* Input          : None
* Output         : None
* Return         : The MSD status.
*******************************************************************************/
u16 MSD_GetStatus(void)
{
    u16 Status = 0;

    /* MSD chip select low */
    MSD_CS_LOW();
    /* Send CMD13 (MSD_SEND_STATUS) to get MSD status */
    MSD_SendCmd(MSD_SEND_STATUS, 0, 0xFF);

    Status = MSD_ReadByte();
    Status |= (u16)(MSD_ReadByte() << 8);

    /* MSD chip select high */
    MSD_CS_HIGH();
    /* Send dummy byte 0xFF */
    MSD_WriteByte(DUMMY);

    return Status;
}

/*******************************************************************************
* Function Name  : MSD_GoIdleState
* Description    : Put MSD in Idle state.
* Input          : None
* Output         : None
* Return         : The MSD Response: - MSD_RESPONSE_FAILURE: Sequence failed
*                                    - MSD_RESPONSE_NO_ERROR: Sequence succeed
*******************************************************************************/
u8 MSD_GoIdleState(void)
{
    /* MSD chip select low */
    MSD_CS_LOW();
    /* Send CMD0 (GO_IDLE_STATE) to put MSD in SPI mode */
    MSD_SendCmd(MSD_GO_IDLE_STATE, 0, 0x95);

    /* Wait for In Idle State Response (R1 Format) equal to 0x01 */
    if (MSD_GetResponse(MSD_IN_IDLE_STATE))
    {
        /* No Idle State Response: return response failue */
        return MSD_RESPONSE_FAILURE;
    }
    /*----------Activates the card initialization process-----------*/
    do
    {
        /* MSD chip select high */
        MSD_CS_HIGH();
        /* Send Dummy byte 0xFF */
        MSD_WriteByte(DUMMY);

        /* MSD chip select low */
        MSD_CS_LOW();

        /* Send CMD1 (Activates the card process) until response equal to 0x0 */
        MSD_SendCmd(MSD_SEND_OP_COND, 0, 0xFF);
        /* Wait for no error Response (R1 Format) equal to 0x00 */
    }
    while (MSD_GetResponse(MSD_RESPONSE_NO_ERROR));

    /* MSD chip select high */
    MSD_CS_HIGH();
    /* Send dummy byte 0xFF */
    MSD_WriteByte(DUMMY);

    return MSD_RESPONSE_NO_ERROR;
}

/*******************************************************************************
* Function Name  : MSD_WriteByte
* Description    : Write a byte on the MSD.
* Input          : Data: byte to send.
* Output         : None
* Return         : None.
*******************************************************************************/
void MSD_WriteByte(u8 Data)
{
    /* Wait until the transmit buffer is empty */
    while (SPI_I2S_GetFlagStatus(MSD_SPI, SPI_I2S_FLAG_TXE) == RESET);
    /* Send the byte */
    SPI_I2S_SendData(MSD_SPI, Data);

    /*!< Wait to receive a byte*/
    while (SPI_I2S_GetFlagStatus(MSD_SPI, SPI_I2S_FLAG_RXNE) == RESET);
    /*!< Return the byte read from the SPI bus */
    SPI_I2S_ReceiveData(MSD_SPI);
}

/*******************************************************************************
* Function Name  : MSD_ReadByte
* Description    : Read a byte from the MSD.
* Input          : None.
* Output         : None
* Return         : The received byte.
*******************************************************************************/
u8 MSD_ReadByte(void)
{
    u8 Data = 0;

    /* Wait until the transmit buffer is empty */
    while (SPI_I2S_GetFlagStatus(MSD_SPI, SPI_I2S_FLAG_TXE) == RESET);
    /* Send the byte */
    SPI_I2S_SendData(MSD_SPI, DUMMY);

    /* Wait until a data is received */
    while (SPI_I2S_GetFlagStatus(MSD_SPI, SPI_I2S_FLAG_RXNE) == RESET);
    /* Get the received data */
    Data = SPI_I2S_ReceiveData(MSD_SPI);

    /* Return the shifted data */
    return Data;
}

/*******************************************************************************
* Function Name  : SPI_Config
* Description    : Initializes the SPI and CS pins.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void SPI_Config(void)
{
    uint32_t delay;
    GPIO_InitTypeDef  GPIO_InitStructure;
    SPI_InitTypeDef   SPI_InitStructure;

    /* GPIOA and GPIOC Periph clock enable */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_MSD_CS | RCC_APB2Periph_SD_PWR, ENABLE);
    /* SPI Periph clock enable */
    RCC_APB2PeriphClockCmd(MSD_RCC_SPI, ENABLE);

    /* Configure SPI pins: SCK, MISO and MOSI */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* Configure SD Power */
    GPIO_InitStructure.GPIO_Pin = SD_PWR_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(SD_PWR_PORT, &GPIO_InitStructure);

    /* Configure SD CS pin */
    GPIO_InitStructure.GPIO_Pin = MSD_CS_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(MSD_CS_PORT, &GPIO_InitStructure);

    /* SPI Config */
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    SPI_Init(MSD_SPI, &SPI_InitStructure);

    /* SPI enable */
    SPI_Cmd(MSD_SPI, ENABLE);

    /* active SD card */
    sd_power_on();

    for (delay = 0; delay < 0xfffff; delay ++);
}

/******************* (C) COPYRIGHT 2008 STMicroelectronics *****END OF FILE****/

/*
 * RT-Thread SD Card Driver
 * 2009-04-17 Bernard first version
 * 2010-07-15 Modify read/write according new block driver interface
 */
#include <rtthread.h>
#include <dfs_fs.h>

static struct rt_device sdcard_device;
static struct dfs_partition part;

#define SECTOR_SIZE 512

/* RT-Thread Device Driver Interface */
static rt_err_t rt_msd_init(rt_device_t dev)
{
    sMSD_CSD MSD_csd;
    MSD_GetCSDRegister(&MSD_csd);

    return RT_EOK;
}

static rt_err_t rt_msd_open(rt_device_t dev, rt_uint16_t oflag)
{
    return RT_EOK;
}

static rt_err_t rt_msd_close(rt_device_t dev)
{
    return RT_EOK;
}

static rt_size_t rt_msd_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
    rt_uint8_t status;
    rt_uint32_t i;

    status = MSD_RESPONSE_NO_ERROR;
    // rt_kprintf("read: 0x%x, size %d\n", pos, size);

    /* read all sectors */
    for (i = 0; i < size; i ++)
    {
        status = MSD_ReadBlock((rt_uint8_t *)((rt_uint8_t *)buffer + i * SECTOR_SIZE),
                               (part.offset + pos + i) * SECTOR_SIZE, SECTOR_SIZE);
        if (status != MSD_RESPONSE_NO_ERROR)
        {
            rt_kprintf("sd card read failed\n");
            return 0;
        }
    }

    if (status == MSD_RESPONSE_NO_ERROR) return size;

    rt_kprintf("read failed: %d\n", status);
    return 0;
}

static rt_size_t rt_msd_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
    rt_uint8_t status;
    rt_uint32_t i;

    status = MSD_RESPONSE_NO_ERROR;
    // rt_kprintf("write: 0x%x, size %d\n", pos, size);

    /* write all sectors */
    for (i = 0; i < size; i ++)
    {
        status = MSD_WriteBuffer((rt_uint8_t *)((rt_uint8_t *)buffer + i * SECTOR_SIZE),
                                 (part.offset + pos + i) * SECTOR_SIZE, SECTOR_SIZE);
        if (status != MSD_RESPONSE_NO_ERROR)
        {
            rt_kprintf("sd card write failed\n");
            return 0;
        }
    }

    if (status == MSD_RESPONSE_NO_ERROR) return size;

    rt_kprintf("write failed: %d\n", status);
    return 0;
}

static rt_err_t rt_msd_control(rt_device_t dev, rt_uint8_t cmd, void *args)
{
    RT_ASSERT(dev != RT_NULL);

    return RT_EOK;
}

/* init SD detected pin */
static void sd_detectpin_init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;

    /* GPIOG Periph clock enable */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SD_CD, ENABLE);

    /* Configure SD detected pin */
    GPIO_InitStructure.GPIO_Pin = SD_CD_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(SD_CD_PORT, &GPIO_InitStructure);
}

int SD_IS_INSTALLED(void)
{
    return (GPIO_ReadInputDataBit(SD_CD_PORT,  SD_CD_PIN) == 0);
}

void rt_hw_msd_init()
{
    sd_detectpin_init();

    /* check SD CARD inserted or not */
    if (!SD_IS_INSTALLED())
    {
        rt_kprintf("SD CARD not inserted!\n");
        return;
    }

    if (MSD_Init() == MSD_RESPONSE_NO_ERROR)
    {
        rt_uint8_t status;
        rt_uint8_t *sector;

        /* register sdcard device */
        sdcard_device.init  = rt_msd_init;
        sdcard_device.open  = rt_msd_open;
        sdcard_device.close = rt_msd_close;
        sdcard_device.read  = rt_msd_read;
        sdcard_device.write = rt_msd_write;
        sdcard_device.control = rt_msd_control;

        /* no private */
        sdcard_device.user_data = RT_NULL;
        /* get the first sector to read partition table */
        sector = (rt_uint8_t *) rt_malloc(512);
        if (sector == RT_NULL)
        {
            rt_kprintf("allocate partition sector buffer failed\n");
            return;
        }

        status = MSD_ReadBlock(sector, 0, 512);
        if (status == MSD_RESPONSE_NO_ERROR)
        {
            /* get the first partition */
            status = dfs_filesystem_get_partition(&part, sector, 0);
            if (status != RT_EOK)
            {
                /* there is no partition table */
                part.offset = 0;
                part.size   = 0;
            }
        }
        else
        {
            /* there is no partition table */
            part.offset = 0;
            part.size   = 0;
        }

        /* release sector buffer */
        rt_free(sector);

        rt_device_register(&sdcard_device, "sd0",
                           RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE | RT_DEVICE_FLAG_STANDALONE);
    }
    else
    {
        rt_kprintf("sdcard init failed\n");
    }
}

unsigned int rt_hw_msd_get_size(void)
{
    unsigned int size_in_bytes;

    if ((part.size>>11) > 0) /* MB */
    {
        unsigned int part_size;
        part_size = part.size >> 11;/* MB */
        size_in_bytes = part_size * (1024 * 1024);
    }
    else
    {
        size_in_bytes = (part.size>>1) * 1024;
    }

    return size_in_bytes;
}
